/* ===================
 * Orson Charts - Demo
 * ===================
 *
 * Copyright 2013-2022, by David Gilbert. All rights reserved.
 *
 * https://github.com/jfree/jfree-demos
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of the JFree organisation nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL OBJECT REFINERY LIMITED BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Note that the above terms apply to the demo source only, and not the
 * Orson Charts library.
 *
 */

package me.renyaoxiang.algorithm.renderer;


import me.renyaoxiang.algorithm.sort.*;
import me.renyaoxiang.algorithm.sortforstats.*;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

public class BarCharGenerator {
    String title = "";
    String chartName = "chart.png";
    List<Class<?>> classList = List.of(
            ShellSort.class,
            BubbleSort.class,
            SelectSort.class,
            InsertSort.class,
            QuickSort.class,
            MergeSort.class,
            ImprovedShellSort.class,
            HeapSort.class
    );
    Map<Class<?>, Class<?>> statsAlgorithmMap = Map.of(
            ShellSort.class, ShellSortForStats.class,
            BubbleSort.class, BubbleSortForStats.class,
            SelectSort.class, SelectSortForStats.class,
            InsertSort.class, InsertSortForStats.class,
            QuickSort.class, QuickSortForStats.class,
            MergeSort.class, MergeSortForStats.class,
            ImprovedShellSort.class, ImprovedShellSortForStats.class,
            HeapSort.class, HeapSortForStats.class

    );

    void generateImage(String title, int count) {
        this.title = title;
        chartName = "chart-" + count + ".png";
        Map<Class<?>, int[]> inputDataMap = createInputDataMap(count);
        Map<Class<?>, Class<?>> statsAlgorithmMap = createStatsAlgorithmMap();
        Map<Class<?>, StatsContext> statsContextMap = createStatsContextContext(inputDataMap, statsAlgorithmMap);
        CategoryDataset dataset = createDataset(statsContextMap);
        JFreeChart barChart = createChart(dataset);
        createImage(barChart);
    }

    private Map<Class<?>, Class<?>> createStatsAlgorithmMap() {
        return statsAlgorithmMap;
    }

    private void createImage(JFreeChart barChart) {
        try {
            ChartUtils.saveChartAsPNG(new File("target" + File.separator + chartName), barChart, 2100, 1600);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    Map<Class<?>, int[]> createInputDataMap(int count) {
        int[] inputData0 = ArrayUtils.shuffle(ArrayUtils.range(0, count));
        Map<Class<?>, int[]> inputDataMap = new HashMap<>();
        classList.forEach(it -> inputDataMap.put(it, ArrayUtils.cloneArray(inputData0)));
        return inputDataMap;
    }

    Map<Class<?>, StatsContext> createStatsContextContext(Map<Class<?>, int[]> inputData, Map<Class<?>, Class<?>> statsAlgorithmMap) {
        Map<Class<?>, StatsContext> statsContextMap = new HashMap<>();
        classList.forEach(it -> {
            StatsContext statsContext = StatsContext.start(context -> {
                try {
                    statsAlgorithmMap.get(it).getMethod("sort", StatsContext.class, int[].class).invoke(null, context, inputData.get(it));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
            });
            statsContextMap.put(it, statsContext);

        });
        return statsContextMap;
    }

    private CategoryDataset createDataset(Map<Class<?>, StatsContext> statsContextMap) {

        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        classList.forEach(it ->
                dataset.setValue(statsContextMap.get(it).getIntValue(Steps.COMPARE), Steps.COMPARE, it.getSimpleName())
        );
        classList.forEach(it ->
                dataset.setValue(statsContextMap.get(it).getIntValue(Steps.SIGN), Steps.SIGN, it.getSimpleName())
        );
        classList.forEach(it ->
                dataset.setValue(statsContextMap.get(it).getIntValue(Steps.INDEX_COMPARE), Steps.INDEX_COMPARE, it.getSimpleName())
        );
        classList.forEach(it ->
                dataset.setValue(statsContextMap.get(it).getIntValue(Steps.INDEX_SIGN), Steps.INDEX_SIGN, it.getSimpleName())
        );
        return dataset;
    }

    private JFreeChart createChart(CategoryDataset dataset) {
        JFreeChart barChart = ChartFactory.createStackedBarChart(
                title,
                "Algorithms",
                "Call",
                dataset,
                PlotOrientation.VERTICAL,
                true, true, false);

        return barChart;
    }

    public static void main(String[] args) {
        IntStream.range(1, 10).forEach(it -> {
            int count = it * 1000;
            new BarCharGenerator().generateImage("Sort_" + count + "", count);
        });
    }
}

